#ifndef EOS_FILTER_SYNERGISM_H
#define EOS_FILTER_SYNERGISM_H
//------------------------------------------------------------------------------
// Copyright 2006 Tom Haines

// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.


/// \file synergism.h
/// Provides a segmentation algorithm with a very silly name.

#include "eos/types.h"
#include "eos/svt/var.h"
#include "eos/svt/field.h"
#include "eos/bs/colours.h"
#include "eos/time/progress.h"

namespace eos
{
 namespace filter
 {
//------------------------------------------------------------------------------
/// This is an implimentation of the algorithm in 'Synergism in Low Level Vision'
/// by Christopher M Christoudias, Bogdan Georgescu and Peter Meer. Segments an
/// image. A run-once class is provided, where you declare the class, fill 
/// in the input by calling various methods, call Run() before finally extracting
/// the output through various methods. The class provides accesss to some 
/// intermediate values.
/// Note the current implimentation is wrong, will remove this message 
/// when fixed. (Whilst wrong, it is extremelly close, and perfactly usable.)
class EOS_CLASS Synergism
{
 public:
  /// &nbsp;
   Synergism()
   :diffRad(2),edgeMix(0.3),spatialSize(7.0),rangeSize(4.5),mergeCutoff(4.5*0.5),minSegment(20),averageSteps(2),mergeMax(0.9),
   out(null<svt::Var*>())
   {}

  /// &nbsp;
   ~Synergism()
   {delete out;}


  /// Sets the image to be proccessed, requires the image in both greyscale and
  /// Luv format as in the paper on which its based. Provided seperatly so you 
  /// can do the most appropriate conversion. (Ushally you will calculate both 
  /// from RGB, problem is there are several ways of doing it.) This is the 
  /// only method you must call before Run().
   void SetImage(const svt::Field<bs::ColourL> & in1,const svt::Field<bs::ColourLuv> & in2) {grey = in1; image = in2;}


  /// The radius (i.e. the window is radius*2 + 1 in size) of the differentiation
  /// filter used for the edge detection part. Default is 2.
   void DiffWindow(nat32 radius) {diffRad = radius;}

  /// Sets the mix between the two values of the edge detector, [0..1]. Defaults
  /// to 0.3.
   void SetMix(real32 a) {edgeMix = a;}

  /// The size of the window used in the mean shift for the non-colour part,
  /// i.e. how large the averaging window on the image is. Defaults to 7.0
   void SetSpatial(real32 s) {spatialSize = s;}

  /// The size of the window used in the mean shift for the colour part, i.e.
  /// how far away the colours it conciders are. Defaults to 4.5
   void SetRange(real32 c) {rangeSize = c; mergeCutoff = c*0.5;}

  /// The cutoff distance for merging. Each time you call SetRange this is set
  /// to half the range, you can use this to set it independently if you want 
  /// however. Defaults to 4.5/2.
   void SetCutoff(real32 c) {mergeCutoff = c;}

  /// Sets the minimum segment size, any segments smaller than this will be merged
  /// whether they like it or not. Defaults to 20.
   void SetSegmentMin(nat32 m) {minSegment = m;}

  /// Sets the number of averaging steps done by the segmenter, defaults to 2 and
  /// should probably not be touched.
   void SetAverageSteps(nat32 n) {averageSteps = n;}

  /// Sets the cutoff which border weights have to be below for a merge to happen,
  /// defaults to 0.9. [0..1] please, though you want to avoid the extremes of 
  /// that range.
   void SetMergeMax(real32 m) {mergeMax = m;}


  /// Changes the class from setup mode to result extraction mode, doing the 
  /// calculations neccesary.
   void Run(time::Progress * prog = null<time::Progress*>());

  
  /// Returns the number of segments found.  
   nat32 Segments() const {return segments;}

  /// Extracts the resulting segmentation. Must be the same size as the input image.
   void GetSegments(svt::Field<nat32> & out) const;

  /// Extracts the mean shift smoothed image generated by the proccess. Must be 
  /// the same size as the input image.
   void GetSmoothed(svt::Field<bs::ColourLuv> & out) const;

  /// Extracts the weight map created by the edge segmentation and used in the mean 
  /// shift algorithm. Must be the same size as the input image.
   void GetWeights(svt::Field<real32> & out) const;

  /// Extracts the eta component of the weight map.
   void GetEta(svt::Field<real32> & out) const;
   
  /// Extracts the rho component of the weight map.
   void GetRho(svt::Field<real32> & out) const;


  /// &nbsp;
   inline cstrconst TypeString() const {return "eos::filter::Synergism";}


 private:
  // Input...
   svt::Field<bs::ColourL> grey;  
   svt::Field<bs::ColourLuv> image;

   nat32 diffRad; // diffRad*2 + 1 is size of differentiation window.
   real32 edgeMix; // The ratio in which the edge details are combined to create the weight map.
   real32 spatialSize; // The size of the spatial window.
   real32 rangeSize; // The size of the range window.
   real32 mergeCutoff; // Distance between two segments must be less than this for them to be merged.
   nat32 minSegment; // Smallest segment size in pixels.
   nat32 averageSteps; // Number of averaging steps in fusion step.
   real32 mergeMax; // Maximum average of weight map on a border for merging to be allowed.

  // Output...
   nat32 segments; // Number of segments found.
   svt::Var * out; // Also includes a load of intermediate values.
   
  // Distance function, to modify the mean shift behaviour to distinguish between
  // colour and position...
   static bit Distance(nat32 fvSize,real32 * fv1,real32 * fv2,real32 pt);
};

//------------------------------------------------------------------------------
 };
};
#endif
